﻿using MF_Base.Model;
using MF_WorkFlow;
using MF_WorkFlow.Model;
using MFTool;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using UIBase;

namespace WorkFlow.Controllers
{
    [Export]
    public class WFFormController : AutoUI.Areas.ConfigUI.Controllers.FormController
    {
        public ActionResult WFPageView(string UICode)
        {
            string formInstId = QueryString("Id");
            string currentStepId = "";
            ViewBag.CanSave = false;//当前环节是否可以保存数据
            ViewBag.CanWithdraw = false;//当前环节是否可以撤回
            ViewBag.CanGoBack = false;//当前环节是否可以退回
            ViewBag.CanOperate = false;//可否操作
            ViewBag.CanTrace = false;//可否跟踪
            ViewBag.bUserComment = false;//可否意见

            //首环节
            if (string.IsNullOrEmpty(formInstId))
            {
                UICode.CheckNotNullOrEmpty("编号UICode不能为空");
                var wfDef = UnitOfWork.GetSingle<WFDef, double>(d => d.OrderIndex, false, a => a.Code == UICode);
                wfDef.CheckNotNull("未找到编号为【" + UICode + "】的流程定义", true);
                if (wfDef.IsPublish.ToLower() != "true")
                {
                    throw new BusinessException("流程还未发布");
                }
                var wfDefInst = UnitOfWork.GetSingle<WFDefInst, double>(d => d.OrderIndex, false, a => a.Code == UICode);
                wfDefInst.CheckNotNull("未找到编号为【" + UICode + "】的流程实例定义");

                ViewBag.WFDefInstId = wfDefInst.Id;
                var wfNodeStart = wfDefInst.GetStartNode();
                wfNodeStart.CheckNotNull("流程起点为空");


                ViewBag.NextRoutingDefInstList = GetNextRoutingDefInstsByNodeDefInstId(wfNodeStart.Id);

                ViewBag.CanOperate = true;
                ViewBag.CanGoBack = false;
                ViewBag.CanSave = (wfNodeStart.CanSave == "1");
                ViewBag.bUserComment = (wfNodeStart.NeedSign == "1");
            }
            //非首环节
            else
            {
                ViewBag.CanTrace = true;

                var wfInst = UnitOfWork.GetSingle<WFInst>(a => a.FormInstId == formInstId);
                if (wfInst == null) throw new BusinessException("流程未发起");

                UICode = wfInst.WDefInst.Code;
                ViewBag.WFDefInstId = wfInst.WFDefInstId;
                var currentStep = wfInst.GetCurrentStep();

                //TODO:流程已执行完毕的判断
                if (currentStep == null)
                {

                }
                else
                {
                    currentStepId = currentStep.Id;

                    ViewBag.CanSave = (currentStep.WFNodeDefInst.CanSave == "1");
                    ViewBag.CanGoBack = (currentStep.WFNodeDefInst.CanGoBack == "1");
                    ViewBag.bUserComment = (currentStep.WFNodeDefInst.NeedSign == "1");
                    //环节的指定人可操作
                    if (!string.IsNullOrEmpty(currentStep.StepUserId) &&
                        currentStep.StepUserId.Contains(GetCurrentUserID()))
                    {
                        ViewBag.CanOperate = true;
                    }

                    //前一步骤执行人是否可以撤回
                    if (currentStep.PreStep != null && currentStep.PreStep.OperateUserId == GetCurrentUserID())
                    {
                        ViewBag.CanWithdraw = (currentStep.PreStep.WFNodeDefInst.CanWithdraw == "1");
                    }
                }

                ViewBag.NextRoutingDefInstList = new List<Dictionary<string, object>>();
                if (!string.IsNullOrEmpty(currentStepId))
                    ViewBag.NextRoutingDefInstList = GetNextRoutingDefInstsByStepId(currentStepId);

                //审批信息
                ViewBag.SignDetail = GetSignDetail(wfInst);
            }

            //表单部分逻辑
            GeneralFormViewBagItem(UICode);
            ViewBag.CurrentStepId = currentStepId;

            return View();
        }

        public ActionResult Trace()
        {
            string formInstId = QueryString("Id");
            ViewBag.FormInstId = formInstId;
            formInstId.CheckNotNullOrEmpty("表实例id不能为空");
            var wfInst = UnitOfWork.GetSingle<WFInst>(a => a.FormInstId == formInstId);
            wfInst.CheckNotNull("FormInstId为【" + formInstId + "】流程实例为空");
            var UICode = wfInst.WDefInst.Code;
            //表单部分逻辑
            GeneralFormViewBagItem(UICode);
            return View();
        }

        public JsonResult GoBack()
        {
            var currentStepId = QueryString("currentStepId");
            var userComment = QueryString("userComment");
            currentStepId.CheckNotNullOrEmpty("stepId为空");
            var currentStep = UnitOfWork.GetByKey<WFStep>(currentStepId);
            var step = GeneralPreStep(currentStep);
            UnitOfWork.Add<WFStep>(step);

            currentStep.NextStepId = step.Id;
            currentStep.OperateUserId = GetCurrentUserID();
            currentStep.OperateUserName = GetCurrentUserName();
            currentStep.OperateTime = DateTime.Now;
            currentStep.UserComment = userComment;
            UnitOfWork.Commit();
            return Json("");
        }

        protected override string GetFormDefCode()
        {
            string Id = QueryString("Id");
            if(!string.IsNullOrEmpty(Id))
            {
                var wfInst = UnitOfWork.GetSingle<WFInst>(a => a.FormInstId == Id);
                wfInst.CheckNotNull("FormIstId为{0}的wfInst为空".ReplaceArg(Id));
                return wfInst.WDefInst.Code;
            }

            return base.GetFormDefCode();           
        }

        public JsonResult Withdraw()
        {
            var currentStepId = QueryString("currentStepId");
            currentStepId.CheckNotNullOrEmpty("stepId为空");
            var currentStep = UnitOfWork.GetByKey<WFStep>(currentStepId);
            currentStep.PreStep.CheckNotNull("id为{0}前一步骤不存在".ReplaceArg(currentStep.Id));
            if (currentStep.PreStep.OperateUserId != GetCurrentUserID())
            {
                throw new BusinessException("你不是发起人,无法撤回");
            }

            var step = GeneralPreStep(currentStep);
            UnitOfWork.Add<WFStep>(step);

            currentStep.NextStepId = step.Id;
            currentStep.OperateUserId = GetCurrentUserID();
            currentStep.OperateUserName = GetCurrentUserName();
            currentStep.OperateTime = DateTime.Now;
            currentStep.Remark = "撤回";
            UnitOfWork.Commit();
            return Json("");
        }

        protected virtual void BeforeExcute(WFStep currentStep, WFStep nextStep, Dictionary<string, object> entityDic)
        {

        }

        protected virtual void OnFlowEnd(Dictionary<string, object> entityDic)
        {

        }

        /// <summary>
        /// 不允许该类的派生类重写该方法
        /// </summary>
        /// <param name="isNew"></param>
        /// <param name="dic"></param>
        protected override void BeforeSave(bool isNew, Dictionary<string, object> dic)
        {
            if(isNew)
            {
                BeforeAdd(dic);
            }
            else
            {
                BeforeUpdate(dic);
            }
        }

        public void GoToNextStep(string nextRoutingDefInstId, WFStep currentStep, Dictionary<string, object> entityDic)
        {
            var nextRoutingDefInst = UnitOfWork.GetByKey<WFRoutingDefInst>(nextRoutingDefInstId);
            nextRoutingDefInst.CheckNotNull("id为{0}的WFRoutingDefInst不存在".ReplaceArg(nextRoutingDefInstId));

            WFStep nextStep = new WFStep();
            nextStep.Id = GuidHelper.CreateTimeOrderID();
            nextStep.PreStepId = currentStep.Id;
            nextStep.WFNodeDefInstId = nextRoutingDefInst.ENodeDefInstId;
            nextStep.WFInstId = currentStep.WFInstId;

            //如果下一节点不是结束节点,必须给执行人
            var nextNodeDefInst = UnitOfWork.GetByKey<WFNodeDefInst>(nextRoutingDefInst.ENodeDefInstId);
            nextNodeDefInst.CheckNotNull("id为{0}的WFNodeDefInst不存在".ReplaceArg(nextRoutingDefInst.ENodeDefInstId));
            if (nextNodeDefInst.WFNodeType != WFNodeType.End.ToString())
            {
                string nextUserId = QueryString("nextUserId");
                nextUserId.CheckNotNullOrEmpty("nextUserId");
                string nextUserName = QueryString("nextUserName");
                nextUserName.CheckNotNullOrEmpty("nextUserName");
                nextStep.StepUserId = nextUserId;
                nextStep.StepUserName = nextUserName;
                nextStep.Name = nextNodeDefInst.Name;
                entityDic.SetValue("FlowPhase", FlowState.Process.ToString());

                BeforeExcute(currentStep, nextStep, entityDic);
            }
            //结束
            else
            {
                entityDic.SetValue("FlowPhase", FlowState.End.ToString());
                OnFlowEnd(entityDic);
            }

            currentStep.NextStepId = nextStep.Id;//
            currentStep.OperateUserId = GetCurrentUserID();
            currentStep.OperateUserName = GetCurrentUserName();
            currentStep.OperateTime = DateTime.Now;
            currentStep.UserComment = entityDic.GetValue("UserComment");
            UnitOfWork.Add<WFStep>(nextStep);
        }      

        public JsonResult GetTraceList()
        {
            string formInstId = QueryString("FormInstId");
            formInstId.CheckNotNullOrEmpty("表实例id不能为空");
            var wfInst = UnitOfWork.GetSingle<WFInst>(a => a.FormInstId == formInstId);
            wfInst.CheckNotNull("FormInstId为【" + formInstId + "】流程实例为空");
            var res = wfInst.WFStep.Select(a => new
            {
                Name = a.Name,
                Receiver = a.StepUserName,
                ReceiveTime = a.CreateTime,
                Operator = a.OperateUserName,
                OperateTime = a.OperateTime,
                OperateTimeSpan = a.OperateTime == null ? "" :
                string.Format("{0}天,{1}小时,{2}分",
                (a.OperateTime.Value - a.CreateTime).Days,
                (a.OperateTime.Value - a.CreateTime).Hours,
                (a.OperateTime.Value - a.CreateTime).Minutes)
            });
            return Json(res);
        }

        public JsonResult GetStepGraph()
        {
            string formInstId = QueryString("FormInstId");
            formInstId.CheckNotNullOrEmpty("表实例id不能为空");
            var wfInst = UnitOfWork.GetSingle<WFInst>(a => a.FormInstId == formInstId);
            wfInst.CheckNotNull("FormInstId为【" + formInstId + "】流程实例为空");
            var stepList = wfInst.WFStep;
            var nodeDefList = wfInst.WDefInst.WFNodeDefInst;
            var routingList = wfInst.WDefInst.WFRoutingDefInst;

            List<Dictionary<string, object>> nodeDefDicList = new List<Dictionary<string, object>>();
            var currentStep = wfInst.GetCurrentStep();
            if (currentStep != null)
            {
                foreach (var nodeDef in nodeDefList)
                {
                    var dic = nodeDef.ToDictionary();
                    if (currentStep.WFNodeDefInstId == nodeDef.Id)
                    {
                        dic.SetValue("IsCurrent", true);
                    }
                    nodeDefDicList.Add(dic);
                }
            }

            List<Dictionary<string, object>> rountingDicList = new List<Dictionary<string, object>>();
            foreach (var routing in routingList)
            {
                var dic = routing.ToDictionary();
                dic.SetValue("IsFinish", false);
                if (stepList.Any(a => a.WFNodeDefInstId == routing.ENodeDefInstId) 
                    && stepList.Any(a => a.WFNodeDefInstId == routing.SNodeDefInstId))
                {
                    dic.SetValue("IsFinish", true);
                }
                rountingDicList.Add(dic);
            }
            return Json(new { Nodes = nodeDefDicList, Routings = rountingDicList });
        }

        public JsonResult GetNextExcuteUsers(string routingDefInstId)
        {
            var routingDefInst = UnitOfWork.GetByKey<WFRoutingDefInst>(routingDefInstId);
            routingDefInst.CheckNotNull("无法找到Id为{0}的路由实例".ReplaceArg(routingDefInstId));
            var resDicList = new List<Dictionary<string, object>>();
            //NextExcuteUserId
            if (!string.IsNullOrEmpty(routingDefInst.NextExcuteUserId))
            {
                var idArr = routingDefInst.NextExcuteUserId.Split(',');
                var nameArr = routingDefInst.NextExcuteUserName.Split(',');
                for (int i = 0; i < idArr.Length; i++)
                {
                    var dic = new Dictionary<string, object>();
                    dic.SetValue("Id", idArr[i]);
                    dic.SetValue("Name", nameArr[i]);
                    resDicList.Add(dic);
                }
            }
            //NextExcuteUserRoleId
            var roleUsers = UnitOfWork.Get<MF_RoleUser>(a => routingDefInst.NextExcuteUserRoleId.Contains(a.MF_RoleId), "MF_User");
            foreach (var roleUser in roleUsers)
            {
                var dic = new Dictionary<string, object>();
                dic.SetValue("Id", roleUser.MF_UserId);
                dic.SetValue("Name", roleUser.MF_User.RealName);
                resDicList.Add(dic);
            }
            //NextExcuteUserSQLSourceId
            var sqlDataSource = UnitOfWork.GetByKey<SQLDataSource>(routingDefInst.NextExcuteUserSQLSourceId);
            if (sqlDataSource != null)
            {
                DataTable dt = sqlDataSource.GetDataTable();
                if (dt.Columns.Contains(routingDefInst.NextExcuteUserSQLSourceResKeyField)
                    && dt.Columns.Contains(routingDefInst.NextExcuteUserSQLSourceResValueField))
                {
                    foreach (DataRow dr in dt.Rows)
                    {
                        var dic = new Dictionary<string, object>();
                        dic.SetValue("Id", dr[routingDefInst.NextExcuteUserSQLSourceResKeyField]);
                        dic.SetValue("Name", dr[routingDefInst.NextExcuteUserSQLSourceResValueField]);
                        resDicList.Add(dic);
                    }
                }
            }
            var distinctDicList = resDicList.Distinct();
            return Json(new
            {
                Id = string.Join(",", distinctDicList.Select(a => a.GetValue("Id"))),
                Name = string.Join(",", distinctDicList.Select(a => a.GetValue("Name")))
            });
        }

        #region private

        /// <summary>
        /// 业务控制器不重写该方法
        /// </summary>
        /// <param name="dic"></param>
        private void BeforeAdd(Dictionary<string, object> dic)
        {
            //表单FlowPhase字段赋值
            dic.SetValue("FlowPhase", FlowState.Create.ToString());

            string wFDefInstId = QueryString("wFDefInstId");
            if (string.IsNullOrEmpty(wFDefInstId))
                throw new BusinessException("流程定义实例的Id为空,无法操作流程");

            wFDefInstId.CheckNotNullOrEmpty("wFDefInstId");
            var wfDefInst = UnitOfWork.GetByKey<WFDefInst>(wFDefInstId);
            wfDefInst.CheckNotNull("wfDefInst");

            #region 创建流程实例
            WFInst wfInst = new WFInst();
            wfInst.Id = GuidHelper.CreateTimeOrderID();
            wfInst.WFDefInstId = wFDefInstId;

            string expression = wfDefInst.FlowInstNameExpression;
            if (!string.IsNullOrEmpty(expression))
            {
                foreach (var p in dic)
                {
                    expression = expression.Replace("{" + p.Key + "}", p.Value.ToString());
                }
            }

            wfInst.Name = expression;
            wfInst.FormInstId = dic.GetValue("Id");
            wfInst.FlowState = dic.GetValue("FlowPhase");
            UnitOfWork.Add<WFInst>(wfInst);
            #endregion

            #region 增加流程实例开始步骤
            WFStep step = new WFStep();
            step.Id = GuidHelper.CreateTimeOrderID();
            step.WFInstId = wfInst.Id;
            var startNodeDefInst = wfDefInst.GetStartNode();
            startNodeDefInst.CheckNotNull("startNodeDefInst");
            step.WFNodeDefInstId = startNodeDefInst.Id;
            step.Name = startNodeDefInst.Name;
            step.StepUserId = GetCurrentUserID();
            step.StepUserName = GetCurrentUserName();
            UnitOfWork.Add<WFStep>(step);
            #endregion

            if (QueryString("JustSave").ToLower() != "true")
            {
                string nextRoutingDefInstId = QueryString("nextRoutingDefInstId");
                if (!string.IsNullOrEmpty(nextRoutingDefInstId))
                {
                    GoToNextStep(nextRoutingDefInstId, step, dic);
                }
            }
        }

        /// <summary>
        /// 业务控制器不重写该方法
        /// </summary>
        /// <param name="dic"></param>
        private void BeforeUpdate(Dictionary<string, object> dic)
        {
            #region 下一步骤
            string currentStepId = QueryString("currentStepId");
            currentStepId.CheckNotNullOrEmpty("currentStepId");
            var currentStep = UnitOfWork.GetByKey<WFStep>(currentStepId);
            currentStep.CheckNotNull("currentStep");
            if (currentStep.WFNodeDefInst.WFNodeType == WFNodeType.End.ToString())
            {
                throw new BusinessException("该流程已经结束");
            }
            else if (!string.IsNullOrEmpty(currentStep.NextStepId))
            {
                throw new BusinessException("该任务已经执行过了");
            }
            string nextRoutingDefInstId = QueryString("nextRoutingDefInstId");
            nextRoutingDefInstId.CheckNotNullOrEmpty("nextRoutingDefInstId");

            if (QueryString("JustSave").ToLower() != "true")
            {
                GoToNextStep(nextRoutingDefInstId, currentStep, dic);
            }

            #endregion
        }

        private WFStep GeneralPreStep(WFStep currentStep)
        {
            var preStep = currentStep.PreStep;
            preStep.CheckNotNull("前一步骤不存在");

            WFStep step = new WFStep();
            step.Id = GuidHelper.CreateTimeOrderID();
            step.Name = preStep.Name;
            step.WFInstId = preStep.WFInstId;
            step.WFNodeDefInstId = preStep.WFNodeDefInstId;
            step.PreStepId = preStep.PreStepId;
            step.StepUserId = preStep.StepUserId;
            step.StepUserName = preStep.StepUserName;
            return step;
        }

        private IEnumerable<WFNodeDefInst> GetNextNodeDefInstsByStepId(string stepId)
        {
            var step = UnitOfWork.GetByKey<WFStep>(stepId);
            step.CheckNotNull("当前步骤为空");
            var res = UnitOfWork.Get<WFRoutingDefInst>(a => a.SNodeDefInstId == step.WFNodeDefInstId)
                .Select(a => a.E_WFNodeDefInst);
            return res;
        }

        private IEnumerable<WFRoutingDefInst> GetNextRoutingDefInstsByStepId(string stepId)
        {
            var step = UnitOfWork.GetByKey<WFStep>(stepId);
            step.CheckNotNull("当前步骤为空");
            var tmpResList = UnitOfWork.Get<WFRoutingDefInst>(a => a.SNodeDefInstId == step.WFNodeDefInstId);
            List<WFRoutingDefInst> resList = new List<WFRoutingDefInst>();
            foreach (var res in tmpResList)
            {
                string tableName = res.WFDefInst.TableName;
                string moduleName = res.WFDefInst.ModuleName;
                string tableId = step.WFInst.FormInstId;
                var sqlHelper = SqlHelper.CreateSqlHelper(moduleName);
                var resTable = sqlHelper.ExcuteTable("select * from {0} where Id = '{1}'".ReplaceArg(tableName, tableId));
                var dicList = resTable.ToDicList();
                dicList.CheckNotNullOrEmpty("未找到Id为{0}的表{1}数据".ReplaceArg(tableId, tableName));

                if (!string.IsNullOrEmpty(res.PassExpression ?? ""))
                {
                    string express = res.PassExpression.ReplaceBraceContent(dicList[0]);
                    bool dRes = false;
                    if (!Eval.EvalJScript(express, out dRes))
                    {
                        throw new BusinessException("表达式{0}(替换值之前{1})解析失败".ReplaceArg(express, res.PassExpression));
                    }
                    if (dRes)
                        resList.Add(res);
                }
                else
                {
                    resList.Add(res);
                }
            }
            return resList;
        }

        private IEnumerable<WFNodeDefInst> GetNextNodeDefInstsByNodeDefInstId(string wfNodeDefInstId)
        {
            var res = UnitOfWork.Get<WFRoutingDefInst>(a => a.SNodeDefInstId == wfNodeDefInstId)
                .Select(a => a.E_WFNodeDefInst);
            return res;
        }

        private IEnumerable<WFRoutingDefInst> GetNextRoutingDefInstsByNodeDefInstId(string wfNodeDefInstId)
        {
            var res = UnitOfWork.Get<WFRoutingDefInst>(a => a.SNodeDefInstId == wfNodeDefInstId);
            return res;
        }

        private List<Dictionary<string, object>> GetSignDetail(WFInst wfInst)
        {
            var resList = new List<Dictionary<string, object>>();
            var stepList = wfInst.WFStep
                //要签字的且已经有操作过的
                .Where(a => a.WFNodeDefInst.NeedSign == "1" && a.OperateTime != null)
                //有可能一个环节多次经过
                .GroupBy(a => a.WFNodeDefInstId)
                //只取最近的
                .Select(group => group.OrderByDescending(a => a.OrderIndex).FirstOrDefault())
                //审批显示顺序按时间从小到大
                .OrderBy(a => a.OrderIndex).ToList();

            foreach (var step in stepList)
            {
                Dictionary<string, object> dic = new Dictionary<string, object>();
                dic.SetValue("StepSign", step.Name);
                dic.SetValue("UserName", step.OperateUserName);
                dic.SetValue("OperateDate", step.OperateTime == null ? "" : step.OperateTime.Value.ToString("yyyy-MM-dd"));
                dic.SetValue("Opinion", step.UserComment);
                resList.Add(dic);
            }

            //根据环节实例来填充定义节点的实例信息
            return resList;
        }
        #endregion
    }
}
